import java.io.*;
import java.util.Random;

import org.omg.CosNaming.IstringHelper;

public class Cache {
    private int cache_size; /* cache缂撳瓨瀹归噺 */
    private int cache_available;    /*绌洪棽鐨勭紦瀛樺潡*/
    private int cache_mapping_ways; /* cache姣忕粍澶у皬 */
    private int cache_group_num;    /* cache缁勬暟閲� */

    private CacheBlock[][] cacheBlocks;


    private int cache_r_count=0;        //璇籧ache鐨勬鏁帮紱
    private int cache_w_count=0;       //鍐檆ache鐨勬鏁帮紱
    private int cache_mem_read=0;     //cache浠巑em璇绘暟鎹殑娆℃暟锛�
    private int cache_mem_write=0;    //cache鍐檓em鏁版嵁鐨勬鏁帮紱
    private int cache_hit_count=0;      //cache鍛戒腑鐨勬鏁帮紱
    private int cache_miss_count=0;     //cache缂哄け鐨勬鏁帮紱
    private int capacity_miss_count=0;   //cache鍥犱负瀹归噺涓嶈冻瀵艰嚧鐨勭己澶�;
    private int conflict_miss_count=0;    //cache鍥犱负缁勫啿绐侀�犳垚鐨勭己澶憋紱

    public Cache(int cache_size,int cache_available,int cache_mapping_ways,int cache_group_num){
        this.cache_size=cache_size;
        this.cache_available=cache_available;
        this.cache_mapping_ways=cache_mapping_ways;
        this.cache_group_num=cache_group_num;
        cacheBlocks=new CacheBlock[cache_group_num][cache_mapping_ways];
/*        String groupNumber=null;//3位二进制位表示第几组
        String wayNumber=null;//2位二进制位表示组内第几个*/
        for(int i=0;i<cache_group_num;i++){
/*        	groupNumber=Integer.toBinaryString(i);
        	while(groupNumber.length()<3){
        		groupNumber+="0";
        	}*/
        	for(int j=0;j<cache_mapping_ways;j++){
        	    this.cacheBlocks[i][j]=new CacheBlock();
        		/*wayNumber=Integer.toBinaryString(j);
        		while(wayNumber.length()<2){
        			wayNumber+="0";
        		}
        		this.cacheBlocks[i][j].setAddr(groupNumber+wayNumber);//给每一缓存快赋值地址*/
        	}
        }
        /*for(int i=0;i<32;i++){
        	this.cacheBlocks[i]=new CacheBlock();
        	this.cacheBlocks[i].setAddr(Integer.toBinaryString(i));
        	while (this.cacheBlocks[i].getAddr().length()<5){
        		this.cacheBlocks[i].setAddr("0"+this.cacheBlocks[i].getAddr());
        	}
        }*/
        
    }


    @Override
    public String toString() {
        return "Cache{" +

               "cache_r_count=" + cache_r_count +
                ", cache_w_count=" + cache_w_count +"\n"+
                ", cache_mem_read=" + cache_mem_read +
                ", cache_mem_write=" + cache_mem_write +"\n"+
                ", cache_hit_count=" + cache_hit_count +
                ", cache_miss_count=" + cache_miss_count +"\n"+
                ", capacity_miss_count=" + capacity_miss_count +
                ", conflict_miss_count=" + conflict_miss_count +"\n"+
                ",cache_available=" + cache_available +
                '}';
    }


    
    /**
     * 读操作
     * @param mem_addr 8位二进制，表示要读的内存地址号
     * @param replaceMethod 1代表随机替换，2代表fifo
     * @param group 主存块对应哪一组
     * @param tag 主存字块标记
     */
    public void read(String mem_addr,int replaceMethod,int group,String tag){
    	this.cache_r_count++;//读缓存快
    	
    	//1.读命中
    	for(int i=0;i<this.cache_mapping_ways;i++){
    		if(this.cacheBlocks[group][i].getTag().equals(tag)){
    			if(!this.cacheBlocks[group][i].getState().equals(State.INVALID)){
    				this.cache_hit_count++;
                    System.out.println("group="+group+";i="+i+";state="+this.cacheBlocks[group][i].getState()+";tag="
                            +this.cacheBlocks[group][i].getTag()+";count"+this.cacheBlocks[group][i].getFifo_count());
    				return;
    				
    			}
    		}
    	}

    	//2.读未命中
    	this.cache_miss_count++;//未命中
    	this.cache_mem_read++;//未命中需要去主存读取
        if(this.cache_available>0){
            this.conflict_miss_count++;//有空闲块造成的冲突缺失
        }else {
            this.capacity_miss_count++;
        }

    	int free=countFreeBlock(group);//有几个空闲块
    	
    	//2.1当前缓存组中有空闲
    	if(free>0){
	    	int i=this.selectFreeBlock(free,group);
            this.cache_available--;
            this.cacheBlocks[group][i].setTag(tag);
            this.cacheBlocks[group][i].setState(State.VALID);
            this.updateClockCount();

            System.out.println("group="+group+";i="+i+";state="+this.cacheBlocks[group][i].getState()+";tag="
                    +this.cacheBlocks[group][i].getTag()+";count"+this.cacheBlocks[group][i].getFifo_count());
            return;
	    				



    	}
    	
    	//2.2当前缓存组中无空闲，采用替换策略
    	else if(free==0){
    		//随机替换
            int i;
            if (replaceMethod==1){
                i=randomReplace(group,tag);
            }
            else{
                i=fifoReplace(group,tag);
            }
            //写回法读的时候要判断数据是否被修改过
            if(this.cacheBlocks[group][i].getState().equals(State.WRITE_BACK)){
                this.cache_mem_write++;
            }
            this.cacheBlocks[group][i].setState(State.VALID);
            this.cacheBlocks[group][i].setTag(tag);
            this.cacheBlocks[group][i].setFifo_count(0);
            this.updateClockCount();

            System.out.println("group="+group+";i="+i+";state="+this.cacheBlocks[group][i].getState()+";tag="
                    +this.cacheBlocks[group][i].getTag()+";count"+this.cacheBlocks[group][i].getFifo_count());

    	}

    }



    /**
     * 写操作
     * @param mem_addr
     * @param replaceMethod
     * @param group
     * @param tag
     */
    public void write(String mem_addr,int replaceMethod,int group,String tag){
        this.cache_w_count++;
        //1.写命中
        for(int i=0;i<this.cache_mapping_ways;i++){
            if(this.cacheBlocks[group][i].getTag().equals(tag)){
                if(!this.cacheBlocks[group][i].getState().equals(State.INVALID)){
                    this.cache_hit_count++;
                    this.cacheBlocks[group][i].setState(State.WRITE_BACK);
                    System.out.println("group="+group+";i="+i+";state="+this.cacheBlocks[group][i].getState()+";tag="
                            +this.cacheBlocks[group][i].getTag()+";count"+this.cacheBlocks[group][i].getFifo_count());
                    return;

                }
            }
        }

        //2.写未命中

        this.cache_miss_count++;//未命中
        this.cache_mem_read++;//未命中需要去主存读取
        if(this.cache_available>0){
            this.conflict_miss_count++;//有空闲块造成的冲突缺失
        }else {
            this.capacity_miss_count++;
        }

        int free=this.countFreeBlock(group);//这组有几个空闲块


        //2.1当前缓存组中有空闲
        if(free>0){
            int i=this.selectFreeBlock(free,group);
            this.cache_available--;
            this.cacheBlocks[group][i].setTag(tag);
            this.cacheBlocks[group][i].setState(State.WRITE_BACK);
            this.updateClockCount();
            System.out.println("group="+group+";i="+i+";state="+this.cacheBlocks[group][i].getState()+";tag="
                    +this.cacheBlocks[group][i].getTag()+";count"+this.cacheBlocks[group][i].getFifo_count());
            return;


        }

        //2.2当前缓存组中无空闲，采用替换策略
        else if(free==0){
            //随机替换
            int i;
            if (replaceMethod==1){
                i=randomReplace(group,tag);
            }
            else{
                i=fifoReplace(group,tag);
            }
            if(this.cacheBlocks[group][i].getState().equals(State.WRITE_BACK)) {
                this.cache_mem_write++;
            }
            this.cacheBlocks[group][i].setState(State.WRITE_BACK);
            this.cacheBlocks[group][i].setTag(tag);
            this.cacheBlocks[group][i].setFifo_count(0);
            this.updateClockCount();
            System.out.println("group="+group+";i="+i+";state="+this.cacheBlocks[group][i].getState()+";tag="
                    +this.cacheBlocks[group][i].getTag()+";count"+this.cacheBlocks[group][i].getFifo_count());
        }

    }

    /**
     * 计算这组有几个空闲块
     * @param group 第几组
     * @return
     */
    public int countFreeBlock(int group){
        int free=0;//这组有几个空闲块
        for(int i=0;i<this.cache_mapping_ways;i++){
            if(this.cacheBlocks[group][i].getState().equals(State.INVALID)){
                free++;
            }
        }
        return free;
    }

    /**
     * 选择哪一块空闲块
     * @param free
     * @param group
     * @return
     */
    public int selectFreeBlock(int free,int group){
        int random=new Random().nextInt(free);//随机抽取第几个空闲块，从0开始

        int count=0;
        for(int i=0;i<this.cache_mapping_ways;i++) {
            if (this.cacheBlocks[group][i].getState().equals(State.INVALID)) {
                if (count== random) {
//                    System.out.println("random="+random+"free="+free+"i="+i);
                    return i;
                }
                count++;
            }
        }
        return 0;
    }


    //随机替换
    public int randomReplace(int group,String tag){
        int random=new Random().nextInt(this.cache_mapping_ways);
        return random;

    }

    //先进先出
    public int fifoReplace(int group,String tag){
        int temp=0;
        int flag=0;
        for (int i=0;i<this.cache_mapping_ways;i++){
            if(temp<this.cacheBlocks[group][i].getFifo_count()){
                temp=this.cacheBlocks[group][i].getFifo_count();
                flag=i;
            }

        }
        return flag;
    }
    
    //缓存块的fifo_count+1
    public void updateClockCount(){
    	for(int i=0;i<this.cache_group_num;i++){
    		for(int j=0;j<this.cache_mapping_ways;j++){
    			if(!this.cacheBlocks[i][j].getState().equals(State.INVALID)){
    				this.cacheBlocks[i][j].setFifo_count(this.cacheBlocks[i][j].getFifo_count()+1);
    			}
    		}
    	}
    }


    /**
     * cpu读写
     * @param path
     * @param replaceMethod
     * @throws Exception
     */
    public  void readAndWrite(String path,int replaceMethod) throws Exception{

        InputStreamReader in=new InputStreamReader(new FileInputStream(path));
        byte[] b=new byte[6];
        BufferedReader bufferedReader=new BufferedReader(in);
        String str;
        while ((str=bufferedReader.readLine())!=null) {
            String[] strings = str.split("\\s+");
            int addr_int = Integer.valueOf(strings[1], 16);//主存地址，8位二进制字符串
            String addr = Integer.toBinaryString(addr_int);
            while(addr.length()<8){
                addr="0"+addr;
            }

            int group=Integer.valueOf(addr.substring(3, 6), 2);//主存块对应哪一组；
            String tag=addr.substring(0,3);//主存字块标记；

            if (strings[0].equals("r")) {
                this.read(addr,replaceMethod,group,tag);

            } else if (strings[0].equals("w")){
                this.write(addr,replaceMethod,group,tag);
            }
        }



    }

    public static void main(String[] args) throws Exception{
        Cache cache=new Cache(32,32,4,8);
        String file="D:/practicing/WebInIdea/src/trace5.txt";
        cache.readAndWrite(file,2);
        System.out.println(cache);
    }

}
